const express = require("express");
const multer = require("multer");
const path = require("path");
const fs = require("fs");
const app = express();
const router = express.Router();
const uploadPath = "./upload"; //分片上传的目录 请确保有该目录
const distPath = "./dist"; //分片合并的目录 请确保有该目录
const upload = multer({ dest: uploadPath });

app.use(express.json());
app.use(express.urlencoded({ extended: false }));

//合并分片
async function mergeFileChunk(folder, name, size, hash) {
  //获取临时文件夹下所有分片
  let chunkPaths = fs.readdirSync(folder);
  //写入到dist文件夹
  let mergefilepath = path.resolve(__dirname, distPath, name);
  if (chunkPaths.length > 0) {
    //对分片排序
    chunkPaths.sort((a, b) => a.split("-$$-")[1] - b.split("-$$-")[1]);
    await Promise.all(
      chunkPaths.map((chunkPath, index) =>
        pipeStream(
          path.resolve(folder, chunkPath),
          //写入流设置为追加 和写入位置
          fs.createWriteStream(mergefilepath, {
            flags: "a",
            start: index * size,
          })
        )
      )
    );
    fs.rmdirSync(folder);
    //把对应关系写入到链表中 为了实现秒传 应写到数据库中
    let txtdata = JSON.stringify({ filename: name, filehash: hash }) + "-$$-";
    fs.writeFile(
      "./filename-hash.list",
      txtdata,
      { flag: "a", encoding: "utf-8" },
      (err) => {
        if (err) {
          console.log(err);
        }
      }
    );
  }
}

//用流读取文件  写入文件
function pipeStream(path, writeStream) {
  return new Promise((resolve, reject) => {
    const readStream = fs.createReadStream(path);
    readStream.on("end", () => {
      fs.unlinkSync(path);
      resolve();
    });
    readStream.on("error", () => {
      console.log("流出现异常");
      reject();
    });
    readStream.pipe(writeStream);
  });
}

router.post("/", (req, res) => {
  res.send("postok");
});

router.get("/", async (req, res) => {
  /* let folder = path.resolve(__dirname, uploadPath, "test");
  if (!fs.existsSync(folder)) {
    fs.mkdirSync(folder);
  } */
  /* let folder = path.resolve(__dirname, uploadPath, "基础班课件.rar");
  await mergeFileChunk(folder, "基础班课件.rar", 20971520); */
  res.send("getok");
});

//上传接口
router.post(
  "/upload",
  upload.fields([{ name: "chunk" }, { name: "hash" }, { name: "filename" }]),
  (req, res) => {
    let { hash, filehash } = req.body;
    let folder = path.resolve(__dirname, uploadPath, filehash);
    //没有同名文件夹则创建文件夹
    if (!fs.existsSync(folder)) {
      fs.mkdirSync(folder);
    }
    let newfilename = `${folder}/${hash}`;
    fs.renameSync(`${uploadPath}/${req.files.chunk[0].filename}`, newfilename);

    res.send("ok");
  }
);

//合并接口
router.post("/merge", async (req, res) => {
  let { filename, size, filehash } = req.body;
  let folder = path.resolve(__dirname, uploadPath, filehash);
  await mergeFileChunk(folder, filename, size, filehash);

  res.send("end");
});

//秒传 检测之前是否上传过改文件
router.post("/contrast", (req, res) => {
  let { filename, filehash, size } = req.body;
  let data = fs.readFileSync("./filename-hash.list", { encoding: "utf-8" });
  let flies = data.split("-$$-");
  flies = flies.slice(0, flies.length - 1);
  let flag = flies.some((r) => {
    let file = JSON.parse(r);
    //也可以只校验文件hash是否一样
    return file.filename === filename && file.filehash === filehash;
  });
  //查看链表文件 发现该文件上传过
  if (flag) {
    res.send({
      msg: "ok",
      data: true,
    });
  } else {
    //尝试去上传文件夹里面找分片文件
    let folder = path.resolve(__dirname, uploadPath, filehash);
    if (fs.existsSync(folder)) {
      let chunkPaths = fs.readdirSync(folder);
      let goodChunk = [];
      //防止把上传一半的分片当成上传完整的
      for (let item of chunkPaths) {
        let chunk = path.resolve(folder, item);
        if (fs.statSync(chunk).size === size) {
          goodChunk.push(item);
        }
      }
      if (goodChunk.length > 0) {
        res.send({
          msg: "ok",
          data: goodChunk,
        });
      } else {
        //之前完全没有上传过该文件
        res.send({
          msg: "ok",
          data: false,
        });
      }
    } else {
      res.send({
        msg: "ok",
        data: false,
      });
    }
  }
});

//获取要下载的文件的大小
router.get('/size/:name',(req,res)=>{
  let filePath = path.resolve(__dirname,distPath,req.params.name)
  //console.log(filePath)
  let size = fs.statSync(filePath).size || null
  console.log('下载文件大小' + size)
  res.send({
    msg:'ok',
    data:size.toString()
  })

})

//文件下载
router.get("/down/:name", (req, res) => {
  let filename = req.params.name;
  let filePath = path.resolve(__dirname, distPath, req.params.name);
  let size = fs.statSync(filePath).size;
  let range = req.headers["range"];
  let file = path.resolve(__dirname, distPath, filename);
  //不使用分片下载  直接传输文件
  if (!range) {
    //res.set({'Accept-Ranges':'bytes'})
    res.set({
      "Content-Type": "application/octet-stream",
      "Content-Disposition": `attachment; filename=${filename}`,
    });
    fs.createReadStream(file).pipe(res);
    return;
  }
  //获取分片的开始和结束位置
  let bytesV = range.split("=");
  bytesV.shift()
  let bytemp = bytesV.join('').split("-");
  let [start, end] = bytemp
  start = Number(start)
  end = Number(end)
  //分片开始 结束位置不对 拒绝下载
  if (start > size || end > size) {
    res.set({ "Content-Range": `bytes */${size}`});
    res.status(416).send(null);
    return;
  }
  //开始分片下载
  res.status(206);
  res.set({
    "Accept-Ranges": "bytes",
    "Content-Range": `bytes ${start}-${end ? end : size}/${size}`,
  });

  console.log(start + '---' + end)
  fs.createReadStream(file, { start, end }).pipe(res);
});

app.use("/", router);

app.listen(3500);
